home *** CD-ROM | disk | FTP | other *** search
/ Chip 2005 August (Alt) / CHIP 2005-08.1.iso / program / guvenlik / syslinux-3.07.exe / unix / syslinux.c < prev    next >
Encoding:
C/C++ Source or Header  |  2005-01-03  |  11.1 KB  |  506 lines

  1. #ident "$Id: syslinux.c,v 1.7 2005/01/04 03:27:43 hpa Exp $"
  2. /* ----------------------------------------------------------------------- *
  3.  *   
  4.  *   Copyright 1998-2005 H. Peter Anvin - All Rights Reserved
  5.  *
  6.  *   This program is free software; you can redistribute it and/or modify
  7.  *   it under the terms of the GNU General Public License as published by
  8.  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
  9.  *   Boston MA 02111-1307, USA; either version 2 of the License, or
  10.  *   (at your option) any later version; incorporated herein by reference.
  11.  *
  12.  * ----------------------------------------------------------------------- */
  13.  
  14. /*
  15.  * syslinux.c - Linux installer program for SYSLINUX
  16.  *
  17.  * This program ought to be portable.  I hope so, at least.
  18.  *
  19.  * This is an alternate version of the installer which doesn't require
  20.  * mtools, but requires root privilege.
  21.  */
  22.  
  23. /*
  24.  * If DO_DIRECT_MOUNT is 0, call mount(8)
  25.  * If DO_DIRECT_MOUNT is 1, call mount(2)
  26.  */
  27. #ifdef __KLIBC__
  28. # define DO_DIRECT_MOUNT 1
  29. #else
  30. # define DO_DIRECT_MOUNT 0    /* glibc has broken losetup ioctls */
  31. #endif
  32.  
  33. #define _GNU_SOURCE
  34. #define _XOPEN_SOURCE 500    /* For pread() pwrite() */
  35. #define _FILE_OFFSET_BITS 64
  36. #include <alloca.h>
  37. #include <errno.h>
  38. #include <fcntl.h>
  39. #include <paths.h>
  40. #include <stdio.h>
  41. #include <string.h>
  42. #include <stdlib.h>
  43. #include <unistd.h>
  44. #include <inttypes.h>
  45. #include <sys/stat.h>
  46. #include <sys/types.h>
  47. #include <sys/wait.h>
  48. #include <sys/mount.h>
  49.  
  50. #include "syslinux.h"
  51. #include "libfat.h"
  52.  
  53. #if DO_DIRECT_MOUNT
  54.  
  55. # include <linux/loop.h>
  56.  
  57. #else
  58.  
  59. # include <paths.h>
  60. # ifndef _PATH_MOUNT
  61. #  define _PATH_MOUNT "/bin/mount"
  62. # endif
  63. # ifndef _PATH_UMOUNT
  64. #  define _PATH_UMOUNT "/bin/umount"
  65. # endif
  66.  
  67. #endif
  68.  
  69. const char *program;        /* Name of program */
  70. const char *device;        /* Device to install to */
  71. pid_t mypid;
  72. char *mntpath = NULL;        /* Path on which to mount */
  73. off_t filesystem_offset = 0;    /* Filesystem offset */
  74. #if DO_DIRECT_MOUNT
  75. int loop_fd = -1;        /* Loop device */
  76. #endif
  77.  
  78. void __attribute__((noreturn)) usage(void)
  79. {
  80.   fprintf(stderr, "Usage: %s [-sf] [-o offset] device\n", program);
  81.   exit(1);
  82. }
  83.  
  84. void __attribute__((noreturn)) die(const char *msg)
  85. {
  86.   fprintf(stderr, "%s: %s\n", program, msg);
  87.  
  88. #if DO_DIRECT_MOUNT
  89.   if ( loop_fd != -1 ) {
  90.     ioctl(loop_fd, LOOP_CLR_FD, 0); /* Free loop device */
  91.     close(loop_fd);
  92.     loop_fd = -1;
  93.   }
  94. #endif
  95.  
  96.   if ( mntpath )
  97.     unlink(mntpath);
  98.  
  99.   exit(1);
  100. }
  101.  
  102. /*
  103.  * read/write wrapper functions
  104.  */
  105. ssize_t xpread(int fd, void *buf, size_t count, off_t offset)
  106. {
  107.   char *bufp = (char *)buf;
  108.   ssize_t rv;
  109.   ssize_t done = 0;
  110.  
  111.   while ( count ) {
  112.     rv = pread(fd, bufp, count, offset);
  113.     if ( rv == 0 ) {
  114.       die("short read");
  115.     } else if ( rv == -1 ) {
  116.       if ( errno == EINTR ) {
  117.     continue;
  118.       } else {
  119.     die(strerror(errno));
  120.       }
  121.     } else {
  122.       bufp += rv;
  123.       offset += rv;
  124.       done += rv;
  125.       count -= rv;
  126.     }
  127.   }
  128.  
  129.   return done;
  130. }
  131.  
  132. ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset)
  133. {
  134.   const char *bufp = (const char *)buf;
  135.   ssize_t rv;
  136.   ssize_t done = 0;
  137.  
  138.   while ( count ) {
  139.     rv = pwrite(fd, bufp, count, offset);
  140.     if ( rv == 0 ) {
  141.       die("short write");
  142.     } else if ( rv == -1 ) {
  143.       if ( errno == EINTR ) {
  144.     continue;
  145.       } else {
  146.     die(strerror(errno));
  147.       }
  148.     } else {
  149.       bufp += rv;
  150.       offset += rv;
  151.       done += rv;
  152.       count -= rv;
  153.     }
  154.   }
  155.  
  156.   return done;
  157. }
  158.  
  159. /*
  160.  * Version of the read function suitable for libfat
  161.  */
  162. int libfat_xpread(intptr_t pp, void *buf, size_t secsize, libfat_sector_t sector)
  163. {
  164.   off_t offset = (off_t)sector * secsize + filesystem_offset;
  165.   return xpread(pp, buf, secsize, offset);
  166. }
  167.  
  168. int main(int argc, char *argv[])
  169. {
  170.   static unsigned char sectbuf[512];
  171.   unsigned char *dp;
  172.   const unsigned char *cdp;
  173.   int dev_fd, fd;
  174.   struct stat st;
  175.   int nb, left;
  176.   int err = 0;
  177.   pid_t f, w;
  178.   int status;
  179.   char mntname[64], devfdname[64];
  180.   char *ldlinux_name, **argp, *opt;
  181.   int force = 0;        /* -f (force) option */
  182.   struct libfat_filesystem *fs;
  183.   struct libfat_direntry dentry;
  184.   libfat_sector_t s, *secp, sectors[65]; /* 65 is maximum possible */
  185.   int32_t ldlinux_cluster;
  186.   int nsectors;
  187.   const char *errmsg;
  188.  
  189.   (void)argc;            /* Unused */
  190.  
  191.   program = argv[0];
  192.   mypid = getpid();
  193.   
  194.   device = NULL;
  195.  
  196.   umask(077);
  197.  
  198.   for ( argp = argv+1 ; *argp ; argp++ ) {
  199.     if ( **argp == '-' ) {
  200.       opt = *argp + 1;
  201.       if ( !*opt )
  202.     usage();
  203.  
  204.       while ( *opt ) {
  205.     if ( *opt == 's' ) {
  206.       syslinux_make_stupid();    /* Use "safe, slow and stupid" code */
  207.     } else if ( *opt == 'f' ) {
  208.       force = 1;        /* Force install */
  209.     } else if ( *opt == 'o' && argp[1] ) {
  210.       filesystem_offset = (off_t)strtoull(*++argp, NULL, 0); /* Byte offset */
  211.     } else {
  212.       usage();
  213.     }
  214.     opt++;
  215.       }
  216.     } else {
  217.       if ( device )
  218.     usage();
  219.       device = *argp;
  220.     }
  221.   }
  222.  
  223.   if ( !device )
  224.     usage();
  225.  
  226.   /*
  227.    * First make sure we can open the device at all, and that we have
  228.    * read/write permission.
  229.    */
  230.   dev_fd = open(device, O_RDWR);
  231.   if ( dev_fd < 0 || fstat(dev_fd, &st) < 0 ) {
  232.     perror(device);
  233.     exit(1);
  234.   }
  235.  
  236.   if ( !force && !S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode) ) {
  237.     die("not a block device or regular file (use -f to override)");
  238.   }
  239.  
  240.   if ( !force && filesystem_offset && !S_ISREG(st.st_mode) ) {
  241.     die("not a regular file and an offset specified (use -f to override)");
  242.   }
  243.  
  244.   xpread(dev_fd, sectbuf, 512, filesystem_offset);
  245.   fsync(dev_fd);
  246.  
  247.   /*
  248.    * Check to see that what we got was indeed an MS-DOS boot sector/superblock
  249.    */
  250.   if( (errmsg = syslinux_check_bootsect(sectbuf)) ) {
  251.     fprintf(stderr, "%s: %s\n", device, errmsg);
  252.     exit(1);
  253.   }
  254.  
  255.   /*
  256.    * Now mount the device.
  257.    */
  258.   if ( geteuid() ) {
  259.     die("This program needs root privilege");
  260.   } else {
  261.     int i = 0;
  262.     struct stat dst;
  263.     int rv;
  264.  
  265.     /* We're root or at least setuid.
  266.        Make a temp dir and pass all the gunky options to mount. */
  267.  
  268.     if ( chdir("/tmp") ) {
  269.       perror(program);
  270.       exit(1);
  271.     }
  272.  
  273. #define TMP_MODE (S_IXUSR|S_IWUSR|S_IXGRP|S_IWGRP|S_IWOTH|S_IXOTH|S_ISVTX)
  274.  
  275.     if ( stat(".", &dst) || !S_ISDIR(dst.st_mode) ||
  276.      (dst.st_mode & TMP_MODE) != TMP_MODE ) {
  277.       die("possibly unsafe /tmp permissions");
  278.     }
  279.  
  280.     for ( i = 0 ; ; i++ ) {
  281.       snprintf(mntname, sizeof mntname, "syslinux.mnt.%lu.%d",
  282.            (unsigned long)mypid, i);
  283.  
  284.       if ( lstat(mntname, &dst) != -1 || errno != ENOENT )
  285.     continue;
  286.  
  287.       rv = mkdir(mntname, 0000);
  288.  
  289.       if ( rv == -1 ) {
  290.     if ( errno == EEXIST || errno == EINTR )
  291.       continue;
  292.     perror(program);
  293.     exit(1);
  294.       }
  295.  
  296.       if ( lstat(mntname, &dst) || dst.st_mode != (S_IFDIR|0000) ||
  297.        dst.st_uid != 0 ) {
  298.     die("someone is trying to symlink race us!");
  299.       }
  300.       break;            /* OK, got something... */
  301.     }
  302.  
  303.     mntpath = mntname;
  304.  
  305. #if DO_DIRECT_MOUNT
  306.     if ( S_ISREG(st.st_mode) ) {
  307.       /* It's file, need to mount it loopback */
  308.       unsigned int n = 0;
  309.       struct loop_info64 loopinfo;
  310.  
  311.       for ( n = 0 ; loop_fd < 0 ; n++ ) {
  312.     snprintf(devfdname, sizeof devfdname, "/dev/loop%u", n);
  313.     loop_fd = open(devfdname, O_RDWR);
  314.     if ( loop_fd < 0 && errno == ENOENT ) {
  315.       die("no available loopback device!");
  316.     }
  317.     if ( ioctl(loop_fd, LOOP_SET_FD, (void *)dev_fd) ) {
  318.       close(loop_fd); loop_fd = -1;
  319.       if ( errno != EBUSY )
  320.         die("cannot set up loopback device");
  321.       else
  322.         continue;
  323.     }
  324.     
  325.     if ( ioctl(loop_fd, LOOP_GET_STATUS64, &loopinfo) ||
  326.          (loopinfo.lo_offset = filesystem_offset,
  327.           ioctl(loop_fd, LOOP_SET_STATUS64, &loopinfo)) )
  328.       die("cannot set up loopback device");
  329.       }
  330.     } else {
  331.       snprintf(devfdname, sizeof devfdname, "/proc/%lu/fd/%d",
  332.            (unsigned long)mypid, dev_fd);
  333.     }
  334.  
  335.     if ( mount(devfdname, mntpath, "msdos",
  336.            MS_NOEXEC|MS_NOSUID, "umask=077,quiet") )
  337.       die("could not mount filesystem");
  338.  
  339. #else
  340.  
  341.     snprintf(devfdname, sizeof devfdname, "/proc/%lu/fd/%d",
  342.          (unsigned long)mypid, dev_fd);
  343.  
  344.     f = fork();
  345.     if ( f < 0 ) {
  346.       perror(program);
  347.       rmdir(mntpath);
  348.       exit(1);
  349.     } else if ( f == 0 ) {
  350.       char mnt_opts[128];
  351.       if ( S_ISREG(st.st_mode) ) {
  352.     snprintf(mnt_opts, sizeof mnt_opts, "rw,nodev,noexec,loop,offset=%llu,umask=077,quiet",
  353.          (unsigned long long)filesystem_offset);
  354.       } else {
  355.     snprintf(mnt_opts, sizeof mnt_opts, "rw,nodev,noexec,umask=077,quiet");
  356.       }
  357.       execl(_PATH_MOUNT, _PATH_MOUNT, "-t", "msdos", "-o", mnt_opts,\
  358.         devfdname, mntpath, NULL);
  359.       _exit(255);        /* execl failed */
  360.     }
  361.  
  362.     w = waitpid(f, &status, 0);
  363.     if ( w != f || status ) {
  364.       rmdir(mntpath);
  365.       exit(1);            /* Mount failed */
  366.     }
  367.     
  368. #endif
  369.   }
  370.   
  371.   ldlinux_name = alloca(strlen(mntpath)+13);
  372.   if ( !ldlinux_name ) {
  373.     perror(program);
  374.     err = 1;
  375.     goto umount;
  376.   }
  377.   sprintf(ldlinux_name, "%s/ldlinux.sys", mntpath);
  378.  
  379.   unlink(ldlinux_name);
  380.   fd = open(ldlinux_name, O_WRONLY|O_CREAT|O_TRUNC, 0444);
  381.   if ( fd < 0 ) {
  382.     perror(device);
  383.     err = 1;
  384.     goto umount;
  385.   }
  386.  
  387.   cdp = syslinux_ldlinux;
  388.   left = syslinux_ldlinux_len;
  389.   while ( left ) {
  390.     nb = write(fd, cdp, left);
  391.     if ( nb == -1 && errno == EINTR )
  392.       continue;
  393.     else if ( nb <= 0 ) {
  394.       perror(device);
  395.       err = 1;
  396.       goto umount;
  397.     }
  398.  
  399.     dp += nb;
  400.     left -= nb;
  401.   }
  402.  
  403.   /*
  404.    * I don't understand why I need this.  Does the DOS filesystems
  405.    * not honour the mode passed to open()?
  406.    */
  407.   fchmod(fd, 0400);
  408.  
  409.   close(fd);
  410.  
  411.   sync();
  412.  
  413. umount:
  414. #if DO_DIRECT_MOUNT
  415.  
  416.   if ( umount2(mntpath, 0) )
  417.     die("could not umount path");
  418.  
  419.   if ( loop_fd != -1 ) {
  420.     ioctl(loop_fd, LOOP_CLR_FD, 0); /* Free loop device */
  421.     close(loop_fd);
  422.     loop_fd = -1;
  423.   }
  424.  
  425. #else
  426.  
  427.   f = fork();
  428.   if ( f < 0 ) {
  429.     perror("fork");
  430.     exit(1);
  431.   } else if ( f == 0 ) {
  432.     execl(_PATH_UMOUNT, _PATH_UMOUNT, mntpath, NULL);
  433.   }
  434.  
  435.   w = waitpid(f, &status, 0);
  436.   if ( w != f || status ) {
  437.     exit(1);
  438.   }
  439.  
  440. #endif
  441.  
  442.   sync();
  443.   rmdir(mntpath);
  444.  
  445.   if ( err )
  446.     exit(err);
  447.  
  448.   /*
  449.    * Now, use libfat to create a block map.  This probably
  450.    * should be changed to use ioctl(...,FIBMAP,...) since
  451.    * this is supposed to be a simple, privileged version
  452.    * of the installer.
  453.    */
  454.   fs = libfat_open(libfat_xpread, dev_fd);
  455.   ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", &dentry);
  456.   secp = sectors;
  457.   nsectors = 0;
  458.   s = libfat_clustertosector(fs, ldlinux_cluster);
  459.   while ( s && nsectors < 65 ) {
  460.     *secp++ = s;
  461.     nsectors++;
  462.     s = libfat_nextsector(fs, s);
  463.   }
  464.   libfat_close(fs);
  465.  
  466.   /*
  467.    * Patch ldlinux.sys and the boot sector
  468.    */
  469.   syslinux_patch(sectors, nsectors);
  470.  
  471.   /*
  472.    * Write the now-patched first sector of ldlinux.sys
  473.    */
  474.   xpwrite(dev_fd, syslinux_ldlinux, 512, filesystem_offset + ((off_t)sectors[0] << 9));
  475.  
  476.   /*
  477.    * Patch the root directory to set attributes to
  478.    * HIDDEN|SYSTEM|READONLY
  479.    */
  480.   {
  481.     const unsigned char attrib = 0x07;
  482.     xpwrite(dev_fd, &attrib, 1, ((off_t)dentry.sector << 9)+dentry.offset+11);
  483.   }
  484.  
  485.   /*
  486.    * To finish up, write the boot sector
  487.    */
  488.  
  489.   /* Read the superblock again since it might have changed while mounted */
  490.   xpread(dev_fd, sectbuf, 512, filesystem_offset);
  491.  
  492.   /* Copy the syslinux code into the boot sector */
  493.   syslinux_make_bootsect(sectbuf);
  494.  
  495.   /* Write new boot sector */
  496.   xpwrite(dev_fd, sectbuf, 512, filesystem_offset);
  497.  
  498.   close(dev_fd);
  499.   sync();
  500.  
  501.   /* Done! */
  502.  
  503.   return 0;
  504. }
  505.  
  506.